package com.example.zluserservice.aspect;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.example.zluserservice.common.base.BaseReqDTO;
import com.example.zluserservice.common.annotation.DataScope;
import com.example.zluserservice.common.constant.CommonConstant;
import com.example.zluserservice.common.util.UserThreadLocal;
import com.example.zluserservice.dto.response.RoleResDTO;
import com.example.zluserservice.dto.response.UserResDTO;
import com.example.zluserservice.service.impl.UserCacheService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;


/**
 * @Author zhenliu（孙凌岳）
 * @Description AOP + 拼接sql来实现控制数据权限
 *              在带有 @DataScope注解的方法执行前，自动判断当前用户的角色数据权限，在参数中添加对应的 SQL条件，从而限制查询结果的可见范围。
 * @Date 2025/6/18 22:04
 * @Version 1.0
 */
@Aspect
@Component
@Slf4j
public class DataScopeAspect {
    //Spring的缓存基于AOP代理实现，同一个类内部的方法调用不会经过代理，导致缓存失效
    @Resource
    private UserCacheService userCacheService;

    /**
     * 配置织入点
     */
    @Pointcut("@annotation(com.example.zluserservice.common.annotation.DataScope)")
    public void dataScopePointCut(){

    }

    /**
     * 预处理 获取当前用户
     * @param point
     * @throws Throwable
     */
    @Before("dataScopePointCut()")
    public void doBefore(JoinPoint point) throws Throwable{
        handleDataScope(point);
    }

    protected void handleDataScope(final JoinPoint joinPoint){
        //获得注解
        DataScope controllerDataScope = getAnnotationLog(joinPoint);

        if(controllerDataScope == null){
            return;
        }

        //获取当前用户
        String subject = UserThreadLocal.getSubject();
        UserResDTO userResDTO = JSONUtil.toBean(subject,UserResDTO.class);
        //如果是超级管理员，则不过滤数据
        if(ObjectUtil.isNotNull(userResDTO) && !userResDTO.getUsername().equals("admin")){
            dataScopeFilter(joinPoint,userResDTO,controllerDataScope.userAlias());
        }
    }

    /**
     * 数据范围过滤器
     * @param joinPoint 切点
     * @param userResDTO 用户
     * @param userAlias 用户别名 （sql查询时候的别名）
     */
    public void dataScopeFilter(JoinPoint joinPoint,UserResDTO userResDTO,String userAlias){
        log.info("******测试过滤数据");
        StringBuilder sqlString = new StringBuilder();

//        for(RoleResDTO roleResDTO : userResDTO.getRoleList()){
            //当前用户对应角色拥有的数据权限
            List<RoleResDTO> roleList = userResDTO.getRoleList();
            String dataScope = roleList.get(0).getDataScope();

            List<Long> normalUserIds = new ArrayList<>();

            //如果是全部数据权限，则不过滤数据
            if(CommonConstant.DATA_SCOPE_ALL.equals(dataScope)){
                //如果是全部数据权限 不拼sql 放行 （超管查看所有用户）
                sqlString = new StringBuilder();
//                break;
            }else if (CommonConstant.DATA_SCOPE_SELF.equals(dataScope)) {//  or u.user_id = 登录用户id
//                log.info("dataScope id 类型: {}", userResDTO.getId().getClass().getName());
//                sqlString.append("(id = ").append(userResDTO.getId()).append(")");   //错误的拼接方式 单库单表可以

                //如果本人数据权限，则只查看自己的数据
                //debug：使用参数占位符来动态拼接
                //针对分库分表的拼接方式，动态拼接来让shardingSphere显示判定id是Long，如果直接拼接会默认Integer
                sqlString.append("(")
                        .append(StrUtil.isNotBlank(userAlias) ? userAlias + "." : " ")
                        .append("id = #{userReqDTO.params.dataScopeUserId})");
            } else if (CommonConstant.DATA_SCOPE_ADMIN.equals(dataScope)) {
                //管理员查看所有普通用户
                //获取所有普通用户ID
                normalUserIds = userCacheService.getNormalUserIds();

                Long userId = UserThreadLocal.getMgtUserId();
                // 包括管理员自己
                if (!normalUserIds.contains(userId)) {
                    normalUserIds.add(userId);
                }

                sqlString.append(StrUtil.isNotBlank(userAlias) ? userAlias + "." : " ");
            }
//        }

        //空字符串也是有意义的 当sqlString == " " 要返回true
        if(StrUtil.isNotEmpty(sqlString.toString())){
            //获取第一个参数，要求一定待是一个BeanEntity，在Service执行前，则已经加上sql or u.user_id = 登录用户id
            Object params = joinPoint.getArgs()[0];

            if(ObjectUtil.isNotNull(params) && params instanceof BaseReqDTO){
                BaseReqDTO baseReqDTO = (BaseReqDTO) params;

                //添加参数化查询条件
                baseReqDTO.getParams().put(CommonConstant.DATA_SCOPE,sqlString);

                if(CommonConstant.DATA_SCOPE_SELF.equals(dataScope)){
                    //动态添加id参数（这样就解析为Long了）
                    baseReqDTO.getParams().put("dataScopeUserId",userResDTO.getId());
                }else if(CommonConstant.DATA_SCOPE_ADMIN.equals(dataScope)){
                    baseReqDTO.getParams().put("dataScopeUserIdList", normalUserIds); // 添加具体 ID 列表
                }
            }
        }
    }

//    /**
//     * 获取所有普通用户ID
//     * 并写入redis  过期时间三十分钟
//     * @return
//     */
//    @Cacheable(value = "USER_DATA-SCOPE_CACHE",cacheManager = "cacheManager30Minutes")
//    public List<Long> getNormalUserIds() {
//        // 使用权限服务的角色标识查询接口
//        return permissionFeignClient.getUserIdsByRoleCode("user");
//    }



    private DataScope getAnnotationLog(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if(method != null){
            return  method.getAnnotation(DataScope.class);
        }

        return null;
    }
}
